ZIP 実装会

ZIP のお勉強として、PHP でバイナリを分解してみる。

とりあえず ZIP を作成する

$ cat yoya1.txt
Hello.
$ cat yoya2.txt
Nice meet you!
$ zip yoya.zip yoya1.txt yoya2.txt
  adding: yoya1.txt (stored 0%)
  adding: yoya2.txt (stored 0%)

幸い無圧縮形式の ZIP ファイルが生成されたので、これを元に調査する事にする

$ hexdump -C yoya.zip 
00000000  50 4b 03 04 0a 00 00 00  00 00 40 72 36 40 51 c4  |PK........@r6@Q.|
00000010  da 37 07 00 00 00 07 00  00 00 09 00 1c 00 79 6f  |.7............yo|
00000020  79 61 31 2e 74 78 74 55  54 09 00 03 08 9c 1b 4f  |ya1.txtUT......O|
00000030  21 9c 1b 4f 75 78 0b 00  01 04 f5 01 00 00 04 14  |!..Oux..........|
00000040  00 00 00 48 65 6c 6c 6f  2e 0a 50 4b 03 04 0a 00  |...Hello..PK....|
00000050  00 00 00 00 49 72 36 40  8e 15 1e 4b 0f 00 00 00  |....Ir6@...K....|
00000060  0f 00 00 00 09 00 1c 00  79 6f 79 61 32 2e 74 78  |........yoya2.tx|
00000070  74 55 54 09 00 03 19 9c  1b 4f 23 9c 1b 4f 75 78  |tUT......O#..Oux|
00000080  0b 00 01 04 f5 01 00 00  04 14 00 00 00 4e 69 63  |.............Nic|
00000090  65 20 6d 65 65 74 20 79  6f 75 21 0a 50 4b 01 02  |e meet you!.PK..|
000000a0  1e 03 0a 00 00 00 00 00  40 72 36 40 51 c4 da 37  |........@r6@Q..7|
000000b0  07 00 00 00 07 00 00 00  09 00 18 00 00 00 00 00  |................|
000000c0  01 00 00 00 a4 81 00 00  00 00 79 6f 79 61 31 2e  |..........yoya1.|
000000d0  74 78 74 55 54 05 00 03  08 9c 1b 4f 75 78 0b 00  |txtUT......Oux..|
000000e0  01 04 f5 01 00 00 04 14  00 00 00 50 4b 01 02 1e  |...........PK...|
000000f0  03 0a 00 00 00 00 00 49  72 36 40 8e 15 1e 4b 0f  |.......Ir6@...K.|
00000100  00 00 00 0f 00 00 00 09  00 18 00 00 00 00 00 01  |................|
00000110  00 00 00 a4 81 4a 00 00  00 79 6f 79 61 32 2e 74  |.....J...yoya2.t|
00000120  78 74 55 54 05 00 03 19  9c 1b 4f 75 78 0b 00 01  |xtUT......Oux...|
00000130  04 f5 01 00 00 04 14 00  00 00 50 4b 05 06 00 00  |..........PK....|
00000140  00 00 02 00 02 00 9e 00  00 00 9c 00 00 00 00 00  |................|
00000150

IO_Bit で分解

IO_Bit で素直に先頭からバイナリを分解してみる。

        while (true) {
            $signature = $reader->getData(4);
            switch ($signature) {
              case "PK\x03\x04": // A. Local file header
                $header = array();
                $header['Signature'] = $signature;
                $header['VersionNeeded'] = $reader->getUI16LE();
<略>

Local Header と Central Header を解きました。

DOS Datetime でハマり

トルエンディアンでひっくり返したうえでビットを切り出すのが正しいのですが、
当初、ビッグエンディアンで初めの 7 bit を切り出して 2012 年だったので、そのまま続けてハマりました。

40 72 36 40 = (2012/1/22 14:18:00) 

続き (2019年3月1日追記)