What You See Is Not What You Get
前回の最後に中身の見えないZIPを入手しました. a3vtyq2e9ua1.hatenablog.com
ZIPファイルフォーマット
定義はここに書いてあります.
https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
以上,終わり! とするのはあまりにお粗末なので,かなりあっさりとですが説明したいと思います.
めちゃめちゃ簡単に言うと,ZIPファイルの中身は以下の3種類に分けられます.
ローカルファイルヘッダ
シグネチャからPK0304ヘッダと表現されることもあり,アーカイブに含まれるファイルの情報が入っています.このヘッダの後に実際の圧縮データが置かれ,基本的にはZIPファイル内に含まれるファイルの数だけ並んでいます.
構造は以下です.
サイズ(byte) | 内容 |
---|---|
4 | シグネチャ(0x504B0304) |
2 | 解凍に必要なバージョン |
2 | 汎用ビットフラグ |
2 | 圧縮方式 |
2 | ファイルの最終変更時間 |
2 | ファイルの最終変更日付 |
4 | CRC-32 |
4 | 圧縮サイズ |
4 | 非圧縮サイズ |
2 | ファイル名の長さ |
2 | 拡張フィールドの長さ |
? | ファイル名 |
? | 拡張フィールド |
この後ろに圧縮されたファイルデータが並びます.
セントラルディレクトリエントリ
セントラルディレクトリエントリはローカルファイルヘッダを拡張したものです.これもシグネチャからPK0102ヘッダと呼ばれることがあります.
サイズ(byte) | 内容 |
---|---|
4 | シグネチャ(0x504B0102) |
2 | 作成に用いたバージョン |
2 | 解凍に必要なバージョン |
2 | 汎用ビットフラグ |
2 | 圧縮方式 |
2 | ファイルの最終変更時間 |
2 | ファイルの最終変更日付 |
4 | CRC-32 |
4 | 圧縮サイズ |
4 | 非圧縮サイズ |
2 | ファイル名の長さ |
2 | 拡張フィールドの長さ |
2 | ファイルコメントの長さ |
2 | ファイル開始のパート番号 |
2 | 内部ファイル属性 |
4 | 外部ファイル属性 |
4 | 対応するPK0304ヘッダの相対オフセット |
? | ファイル名 |
? | 拡張フィールド |
? | ファイルコメント |
これもファイルの数だけ並びます.
セントラルディレクトリエントリの終端レコード
PK0506ヘッダです.大抵ZIPファイルの末尾に置かれ,ファイルに関するあれこれが書いてあります.
サイズ(byte) | 内容 |
---|---|
4 | シグネチャ(0x504B0506) |
2 | 分割パート番号 |
2 | セントラルディレクトリの開始パート番号 |
2 | このパートに含まれるファイル数 |
2 | ZIPアーカイブ全体のファイル数 |
4 | PK0102ヘッダの合計サイズ |
4 | PK0102ヘッダ開始位置のオフセット |
2 | ZIPファイルのコメントの長さ |
? | ZIPファイルのコメント |
例えば,2つのファイルをZIPでアーカイブした場合下のような構造になります.
サンプルを用いてみる
↑のようなZIPファイルを作ってみます.archive.zipにfile1.txtとfile2.txtが入っており,それぞれ「abcdef」「ghijkl」と書いてあります.
このarchive.zipをバイナリエディタで開くと以下のようになります.構造に従い色を分けています.
先ほど示した構造と同じようになっています.ただし,データはリトルエンディアンになっています.
中身が見えないZIPを修復する
前回入手したZIPファイル(file.zip)は,Windowsのexplorerで開くと中身が何もない状態でした.
実はあのZIPファイルのPK0506ヘッダは下のようにシグネチャ以外全て0に書き換えられています.
50 4B 05 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ということでイイ感じに直していきます.
分割パート番号とセントラルディレクトリの開始パート番号は現在ではほとんど使われないので0のままにしておきます. PK0304,PK0102,PK0506ヘッダがそれぞれ1つずつだったので,このZIPファイルに含まれるファイルは1つと考えられます.
50 4B 05 06 00 00 00 00 01 00 01 00 00 00 00 00 00 00 00 00 00 00
下の図から分かる通り,PK0102ヘッダのサイズは92バイト,16進数にすると0x5Cバイトでした.またPK0102ヘッダは0x423Eから始まっています.
これに従ってPK0506ヘッダを書き換えます.
50 4B 05 06 00 00 00 00 01 00 01 00 5C 00 00 00 3E 42 00 00 00 00
さて,PK0506ヘッダを修復したバイナリをfile_fixed.zipとして保存しました.
file_fixed.zipを開いてみるとpicture.pngが出現しました.このまま解凍できます.
ちなみにpicture.pngはこれです.いらすとや様の「豆鉄砲を食った鳩のイラスト」をサイズ変更したものです.
おわり
前回からひと月以上経ちましたが,やっと予告通りZIPについて書きました.
実は,file.zipはexplorerで開くとファイルが見えないのですが,7ZipやLhaplusでは警告こそ出るものの普通に解凍できます.つまりexplorerはPK0506ヘッダのみを見てファイルを判定しているのに対し,7ZipやLhaplusはほかのヘッダも読んで処理しているものと推測できます.
今回取り扱ったのは直にファイルが入っている無圧縮ZIPです.しかし実際には多くの場合ファイルは圧縮されているしディレクトリ構造を持つこともあります.データの整合性を保つためのCRC-32とか様々な圧縮アルゴリズムとかありますが疲れたのでここまでとします.