- The added line is THIS COLOR.
- The deleted line is THIS COLOR.
TITLE: PHP でバイナリプログラミング
http://events.php.gr.jp/events/show/96
#contents
* はじめに [#v18439f5]
PHP でバイナリ処理の話をあまり聞かないので、あえてニッチな所を狙って発表させて頂きます。
- キーワード: Binary、Format, Byte, Bit, Endian, Signed,
- ネタ: pure PHP で画像プロテクト、JPEGからGPS抜き出し、Flash SWF改変…
- デモ: 画像プロテクト、JPEGからGPS抜き出し、Flash SWF改変… (全て pure PHP で)
PHP というより、バイナリの勉強会といった要素が強いですが、
多分…、いつかお役に立てると思いますので、どうか、ご容赦ください。
* 一応、自己紹介 [#l413a7de]
- http://d.hatena.ne.jp/yoya/ で、たまに技術ネタを書いてます。
- http://d.hatena.ne.jp/yoya/ でプログラミングしてて困った事とか書いてます。
- 一昨年位まで、数十万ユーザ規模の携帯サイトでアプリ開発をしていました。
-- PHP では主にバイナリ編集をしていました。フレームワークとかビジネスロジックとかよく知りません。
- 今も、一応 PHP でお仕事してます。
-- その時に、PHP で主にバイナリ編集をしていました。フレームワークとかビジネスロジックとかよく知りません。
- 今も、一応 PHP でお仕事してます。あと、C 言語もたまに使います。
* バイナリの定義 [#vd6a4a1e]
* まずは、バイナリの定義 [#vd6a4a1e]
- Wikipedia より
通常バイナリとテキストは対比して用いられる。
テキストとはデータの内容すべてを人間が読んで理解できる (human-readable) 表現形式を指し、
バイナリとはそうでない表現形式を指すことが多い。
なので、本発表では、バイナリファイルの事を、
テキストエディタで開いて読めない文字とか記号が表示されるようなファイル。
という事にしておきます。
* バイナリの実例 [#z1197160]
% hexdump -C aria.gif
00000000 47 49 46 38 39 61 c8 00 96 00 f7 00 00 00 00 00 |GIF89a..........|
00000010 ff ff ff 96 53 58 29 1b 1c e6 b0 b8 b2 69 76 37 |....SX)......iv7|
00000020 26 29 d6 96 a1 cb c6 c7 34 1c 22 48 31 38 2b 21 |&)......4."H18+!|
<略>
GIF ファイルですね。
$ hexdump -C kuriboo.png
00000000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 |.PNG........IHDR|
00000010 00 00 00 c0 00 00 00 e0 08 06 00 00 00 55 70 69 |.............Upi|
00000020 31 00 00 00 04 73 42 49 54 08 08 08 08 7c 08 64 |1....sBIT....|.d|
<略>
PNG です。
先頭の4文字を見ると何か分かる事が多いのですが、その後ろにはよく分からないデータが続いてます。
このよく分からないデータを PHP で改変する方法について、今回はお話します。
* PHP とバイナリ [#e5d1b7d7]
PHP の string 型でバイナリ処理が簡単に出来る。というのが
今回、紹介する Tips の肝です。
* 本当にバイナリ処理できるの? [#d76edda9]
** \0 は? [#ta2f5d12]
C言語が典型例ですが、\0 を文字列が終わったマークとして使う処理系も
結構あるので、string型をバイナリデータとして使う場合、途中で切れないか。
特に、C言語出身者は心配になると思います。
- 簡単に確認
$s = "This is TEST\n";
$s{3} = "\0";
echo strlen($s)."\n";
echo $s;
13
Thi is TEST
- 確認done
** 8bitスルー? [#e87fe524]
- US-ASCII は 7bit で表現できるので、先頭1bit が特別扱いされたりしない?
$s = ' ';
$s{3} = 'A';
$s{4} = chr(ord('A') | 0x80);
var_dump(bin2hex($s));
string(10) "20202041c1"
- 8bit スルーです。日本語とかも入れられるし当然ですね。
** バイナリを取り込んでそのまま出力 [#q17ca2f5]
- でも、細かい事考えなくても、バイナリファイルを入力して、何も変えずに出力して同じデータが生成されれば、OKですよね。
$data = file_get_contents($argv[1]);
echo $data;
% php echo.php saitama.jpg > output.dat
% md5sum saitama.jpg output.dat
06f741dca38937df3702f6759aead28b saitama.jpg
06f741dca38937df3702f6759aead28b output.dat
- 入力をそのまま出力して、同じデータになりました。
- 後は、この途中でデータを分割したり結合したり、入れ替えたりすれば編集できる事になります。
* byte処理 [#y43196b3]
- String 関数が使えます http://php.net/manual/ja/ref.strings.php
-- strlen (データサイズを調べる。呼ぶたびに文字数を数えたりしないので安心)
-- substr (データから一部を抜き出す)
-- substr_replace (データの一部を入れ替える)
試してみましょう。
** JPEG の解析 [#pac8673a]
*** 初めの一歩 [#fb1c2d0c]
- まずファイルを開いてみる
% hexdump -C aria.jpg
00000000 ff d8 ff e0 00 10 4a 46 49 46 00 01 01 01 00 60 |......JFIF.....`|
00000010 00 60 00 00 ff e1 00 22 45 78 69 66 00 00 49 49 |.`....."Exif..II|
00000020 2a 00 08 00 00 00 01 00 00 51 04 00 01 00 00 00 |*........Q......|
<略>
- 全く分からないので、「JPEG フォーマット 仕様」でググってみる
- http://siisise.net/jpeg.html#format
マーカ・コード 長さ データ
FFxx(16bit) 16bit 可変サイズ
- ''16bit は 2 byte'' 、あと、ffXX で区切るらしい。
00000000 ff d8 ff e0 00 10 ...
~~~~~ ~~~~~~~~~~~~~~~
SOI APP0
- 何となくピンと来始める
*** コードを書き始める [#u317c98f]
-
* bit処理 [#mda8635b]
- 普通は pack を使うのですが… http://jp.php.net/manual/ja/function.pack.php
- 当たり前すぎるので、今回は使わない方法を。。
* その他Tips [#o4b9d5e0]
- PHP の閉じタグ ?> は使わない。?> の後ろに改行やゴミ文字があった場合に、テキストなら最後にゴミが付くだけで大きな問題になりにくいが、バイナリだと致命的。